/* 
 * Copyright (C) 2004-2005 Jonathan Bindel
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace DCSharp.Xml
{
	/// <summary>
	/// A class that can be used to load and save file lists in the ADC XML format.
	/// </summary>
	/// <remarks>
	/// The format is described in section 3.3 of http://dcplusplus.sourceforge.net/ADC.htm
	/// </remarks>
	[XmlRoot("FileListing")]	
	public class FileList
	{
		private List<DirectoryNode> directories;

		private string version;
		private string cid;
		private string generator;
		private string directoryBase;

		public FileList() : this(null)
		{
		}

		public FileList(string cid)
		{
			this.cid = cid;

			version = "1";
			generator = "DC++ 0.674";
			directoryBase = "/";

			directories = new List<DirectoryNode>();
		}

		#region Properties

		[XmlAttribute()]
		public string Version
		{
			get { return version; }
			set { version = value; }
		}

		[XmlAttribute()]
		public string CID
		{
			get { return cid; }
			set { cid = value; }
		}

		[XmlAttribute()]
		public string Generator
		{
			get { return generator; }
			set { generator = value; }
		}

		[XmlAttribute()]
		public string Base
		{
			get { return directoryBase; }
			set { directoryBase = value; }
		}

		[XmlElement("Directory")]
		public List<DirectoryNode> Directories
		{
			get { return directories; }
			set { directories = value; }
		}

		[XmlIgnore()]
		public DirectoryNode this[string name]
		{
			get
			{
				return directories.Find(delegate(DirectoryNode directory)
				{
					return directory.Name == name;
				});
			}
		}

		#endregion

		#region Methods

		public void Add(DirectoryNode directory)
		{
			directories.Add(directory);
		}

		public void Remove(DirectoryNode directory)
		{
			directories.Remove(directory);
		}

		public bool Contains(DirectoryNode directory)
		{
			return directories.Contains(directory);
		}

		public static FileList Create(Stream inStream,
			FileUtil.Compression compression, User user)
		{
			using (Stream decompressed = FileUtil.GetDecompressionStream(
				inStream, compression))
			{
				using (XmlTextReader reader = new XmlTextReader(decompressed))
				{
					FileList fileList = new FileList(user.Uid.ToString());
					fileList.Load(reader);

					return fileList;
				}
			}
		}

		public long GetSize()
		{
			long total = 0;
			foreach (DirectoryNode directory in directories)
			{
				total += directory.GetSize();
			}
			return total;
		}

		public void Save(string filename)
		{
			System.IO.Directory.CreateDirectory(Path.GetDirectoryName(filename));

			XmlSerializer xs = new XmlSerializer(typeof(FileList));

			// Remove namespaces
			XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
			xsn.Add(String.Empty, String.Empty);

			// Omit the byte order mark, as defined by the ADC protocol.
			Encoding encoding = new UTF8Encoding(false);

			using(XmlTextWriter writer = new XmlTextWriter(filename, encoding))
			{
				writer.Formatting = Formatting.Indented;
				writer.IndentChar = '\t';
				writer.Indentation = 1;

				// Include the standalone attribute
				writer.WriteStartDocument(true);

				xs.Serialize(writer, this, xsn);
			}
		}

		public void Load(string filename)
		{
			if (filename == null)
			{
				throw new ArgumentNullException("filename");
			}

			using (XmlTextReader reader = new XmlTextReader(filename))
			{
				Load(reader);
			}
		}

		protected void Load(XmlTextReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}

			// TODO: Load version, generator, etc.
			while (reader.ReadToFollowing("Directory"))
			{
				directories.Add(ReadDirectory(reader));
			}
		}

		private static DirectoryNode ReadDirectory(XmlReader reader)
		{
			DirectoryNode directory = new DirectoryNode(reader.GetAttribute("Name"));

			if (reader.IsEmptyElement)
			{
				return directory;
			}

			while (reader.Read())
			{
				if (reader.Name == "Directory" &&
					reader.NodeType == XmlNodeType.EndElement)
				{
					break;
				}

				if (reader.Name == "Directory" &&
					reader.NodeType == XmlNodeType.Element)
				{
					directory.Directories.Add(ReadDirectory(reader));
				}
				else if (reader.Name == "File" &&
					reader.NodeType == XmlNodeType.Element)
				{
					FileNode file = new FileNode(reader.GetAttribute("Name"),
						long.Parse(reader.GetAttribute("Size")),
						reader.GetAttribute("TTH"));

					directory.Files.Add(file);
				}
			}
			return directory;
		}

		#endregion
	}
}
